﻿ unit DataPacketSend;

interface

uses
  Windows, SysUtils, Protocol, PacketProctol, Engine, MemBuffer,
  CriticalSection, CircularBuffer;

type
  //生成数据包, 并且把数据包加入发送队列

  //把数据包加入发送队列
  TOnSendBufToQueue = procedure(pSendBuffer: TIBuffer) of object;

  TIDataPacketSend = class
  private
    m_LogicLock: TCriticalSection;          //逻辑锁
    m_bLastSendBuf: Boolean;                //是否写上一次未发送的Buffer
    m_pLastSendBuf: TIBuffer;
    m_DISocketEngine: TIEngine;
    FOnSendBufToQueue: TOnSendBufToQueue;
    procedure AddWSASendBufToQueue(FSendBuffer: TIBuffer);
  published
    property OnSendBufToQueue: TOnSendBufToQueue read FOnSendBufToQueue write FOnSendBufToQueue;
  public
    //设置包头帧长度
    procedure SetDPHeadLength(var m_PackHead: TDIGlobalPak_Head; nPackLen: Cardinal);
    //创建包头
    procedure CreateDataPacketHead(var m_PackHead: TDIGlobalPak_Head);
    //创建包体
    procedure CreateDataPacketBody(var m_PackHead: TDIGlobalPak_Head;
      var m_PackBody: TDIGlobalPak_Body; PakLEVEL: Byte; PakDirection: Byte;
      PakKind: Cardinal; PakCmd: Cardinal);
    //写数据包加入队列
    procedure WriteTailDataPacket(pDataPacket: PByte; nDataPacketLen: Cardinal;
      m_bInQueue: Boolean = False);
    //写数据
    procedure WriteDataPacketToBuffer(var m_PackHead: TDIGlobalPak_Head;
      var m_PackBody: TDIGlobalPak_Body;  pLogicDataPack: PByte; nDataPakLen: WORD); overload;
    procedure WriteDataPacketToBuffer(var m_PackHead: TDIGlobalPak_Head;
      var m_PackBody: TDIGlobalPak_Body; pLogicDataPack: PByte; nDataPakLen: WORD;
      pLogicDataPackEx: PByte; nDataPakLenEx: WORD); overload;
    procedure WriteDataPacketToBuffer(var m_PackHead: TDIGlobalPak_Head;
      var m_PackBody: TDIGlobalPak_Body; pResponsePack: PByte;
      nResponsePakLen: Word; pLogicDataPack: PByte; nDataPakLen: WORD;
      pLogicDataPackEx: PByte; nDataPakLenEx: Cardinal); overload;

  public
    constructor Create(FDISocketEngine: TIEngine);
    destructor Destroy; override;
  end;

implementation
  
constructor TIDataPacketSend.Create(FDISocketEngine: TIEngine);
begin
  inherited Create;
  m_bLastSendBuf := False;
  m_pLastSendBuf := nil;
  m_DISocketEngine := FDISocketEngine;
  m_LogicLock := TCriticalSection.Create;
end;

destructor TIDataPacketSend.Destroy;
begin
  FreeAndNil(m_LogicLock);
  inherited Destroy;
end;

procedure TIDataPacketSend.SetDPHeadLength(var m_PackHead: TDIGlobalPak_Head; nPackLen: Cardinal);
begin
  m_PackHead.dPacketLen := nPackLen;
end;

procedure TIDataPacketSend.CreateDataPacketHead(var m_PackHead: TDIGlobalPak_Head);
begin
  ZeroMemory(@m_PackHead, DL_GLOBALPAK_HEAD_LEN);
  m_PackHead.bPakHead := CD_RACKET_HEAD;                        //帧头起始符 64
  m_PackHead.dVersion := CD_RACKET_VERSION;                     //协议版本号 1.0.0.1
end;

procedure TIDataPacketSend.CreateDataPacketBody(var m_PackHead: TDIGlobalPak_Head;
  var m_PackBody: TDIGlobalPak_Body; PakLEVEL: Byte; PakDirection: Byte;
  PakKind: Cardinal; PakCmd: Cardinal);
begin
  CreateDataPacketHead(m_PackHead);
  ZeroMemory(@m_PackBody, DL_GLOBALPAK_BODY_LEN);
  m_PackBody.bPakLEVEL := PakLEVEL;                     //级别(正常)
  m_PackBody.bPakDirection := PakDirection;            //流向
  m_PackBody.bPakKind := PakKind;                     //包类别
  m_PackBody.bPakCmd := PakCmd;                      //包类型
end;

procedure TIDataPacketSend.WriteTailDataPacket(pDataPacket: PByte; nDataPacketLen: Cardinal;
  m_bInQueue: Boolean);
var
  m_bLastPak: Boolean;
  m_pPack: PByte;
  m_nSendLen: Integer;
  m_nDataPacketLen: Integer;
  FSendBuffer: TIBuffer;
begin
  m_nDataPacketLen := nDataPacketLen;
  if not m_bLastSendBuf then   //是否写上一次未发送的Buffer
  begin
    FSendBuffer := m_DISocketEngine.MemoryPool.AllocateBuffer(IO_WSA_SEND); //重新分配一个buf
    if FSendBuffer = nil then Exit;
    if (nDataPacketLen <= MAX_PACKAGESIZE) then //是否一次可以写完
    begin
      FSendBuffer.CreatePackage(pDataPacket, nDataPacketLen);   //一次打包完成
      if (nDataPacketLen = MAX_PACKAGESIZE) or m_bInQueue then  //如果缓冲区满,或者强制加入队列
        AddWSASendBufToQueue(FSendBuffer)
      else
      begin 
        m_pLastSendBuf := FSendBuffer;                          //否则赋值成为最后一个包
        m_bLastSendBuf := True;                                 //有后续包(打包发送)
      end;
    end
    else
    begin
      m_bLastPak := False;
      m_nSendLen := MAX_PACKAGESIZE;                            
      FSendBuffer.CreatePackage(pDataPacket, MAX_PACKAGESIZE);  //生成第一个包,加入发送队列
      AddWSASendBufToQueue(FSendBuffer);
      m_nDataPacketLen := m_nDataPacketLen- MAX_PACKAGESIZE;    //剩余需要写包大小
      m_pPack := pDataPacket;
      while m_nDataPacketLen > 0 do
      begin
        m_pPack := Pointer(LongInt(m_pPack) + m_nSendLen);        //偏移指针
        FSendBuffer := m_DISocketEngine.MemoryPool.AllocateBuffer(IO_WSA_SEND);
        if FSendBuffer = nil then Exit;
        m_nSendLen := MAX_PACKAGESIZE;                          //默认写数据包大小
        //发送包(最大) > 剩余包 为最后一个包
        if m_nSendLen >= m_nDataPacketLen then
        begin
          m_bLastPak := True;                                   //最后一个包标志
          m_nSendLen := m_nDataPacketLen;                       //修改实际发送大小
        end;
        FSendBuffer.CreatePackage(m_pPack, m_nSendLen);
        //如果是最后一个包, 判断是否加入队列(后续数据)
        if not m_bLastPak then
          AddWSASendBufToQueue(FSendBuffer)
        else
        begin
          if m_bInQueue then
            AddWSASendBufToQueue(FSendBuffer)
          else
          begin
            m_pLastSendBuf := FSendBuffer;
            m_bLastSendBuf := True;                             //有后续包(打包发送)
          end;
        end;
        m_nDataPacketLen := m_nDataPacketLen - MAX_PACKAGESIZE;
      end;
    end;
  end
  else
  begin
    //写后续包, 一个包可以发送
    if (m_pLastSendBuf.GetUsed + nDataPacketLen) <= MAX_PACKAGESIZE then
    begin
      m_pLastSendBuf.CreatePackage(pDataPacket, nDataPacketLen);
      //如果无后续包, 则加入队列, 否则标志不变，继续打包
      if m_bInQueue then
      begin
        AddWSASendBufToQueue(m_pLastSendBuf);
        m_bLastSendBuf := False;
      end;
    end
    else
    begin
      //写后续包, 需要多次发送先计算上个包余下的空间
      m_nSendLen := MAX_PACKAGESIZE - m_pLastSendBuf.GetUsed;
      m_pLastSendBuf.CreatePackage(pDataPacket, m_nSendLen);
      AddWSASendBufToQueue(m_pLastSendBuf);
      //计算余下要发送的数据包长度
      m_nDataPacketLen := m_nDataPacketLen- m_nSendLen;
      m_pPack := pDataPacket;
      while m_nDataPacketLen > 0 do
      begin
        m_pPack := Pointer(LongInt(m_pPack) + m_nSendLen);
        FSendBuffer := m_DISocketEngine.MemoryPool.AllocateBuffer(IO_WSA_SEND);
        if FSendBuffer = nil then Exit;
        m_nSendLen := MAX_PACKAGESIZE;
        //发送包(最大) > 剩余包 为最后一个包
        if m_nSendLen >= m_nDataPacketLen then
        begin
          m_bLastPak := True;
          m_nSendLen := m_nDataPacketLen;
        end;
        FSendBuffer.CreatePackage(m_pPack, m_nSendLen);
        //如果是最后一个包, 判断是否加入队列(后续数据)
        if not m_bLastPak then
          AddWSASendBufToQueue(FSendBuffer)
        else
        begin
          if m_bInQueue then
          begin
            AddWSASendBufToQueue(FSendBuffer);
            m_bLastSendBuf := False;
          end
          else
          begin
            m_pLastSendBuf := FSendBuffer;
            m_bLastSendBuf := True;
          END;
        end;
        m_nDataPacketLen := m_nDataPacketLen - MAX_PACKAGESIZE;
      end;
    end;
  end;
end;

procedure TIDataPacketSend.WriteDataPacketToBuffer(var m_PackHead: TDIGlobalPak_Head;
  var m_PackBody: TDIGlobalPak_Body; pLogicDataPack: PByte; nDataPakLen: WORD);
begin
  m_LogicLock.Lock;
  WriteTailDataPacket(@m_PackHead, DL_GLOBALPAK_HEAD_LEN);
  WriteTailDataPacket(@m_PackBody, DL_GLOBALPAK_BODY_LEN);
  WriteTailDataPacket(pLogicDataPack, nDataPakLen, True);
  m_LogicLock.UnLock;
end;

procedure TIDataPacketSend.WriteDataPacketToBuffer(var m_PackHead: TDIGlobalPak_Head;
  var m_PackBody: TDIGlobalPak_Body; pLogicDataPack: PByte;
  nDataPakLen: WORD; pLogicDataPackEx: PByte; nDataPakLenEx: WORD);
begin
  m_LogicLock.Lock;
  WriteTailDataPacket(@m_PackHead, DL_GLOBALPAK_HEAD_LEN);
  WriteTailDataPacket(@m_PackBody, DL_GLOBALPAK_BODY_LEN);
  WriteTailDataPacket(pLogicDataPack, nDataPakLen);
  WriteTailDataPacket(pLogicDataPackEx, nDataPakLenEx, True);
  m_LogicLock.UnLock;
end;

procedure TIDataPacketSend.WriteDataPacketToBuffer(var m_PackHead: TDIGlobalPak_Head;
  var m_PackBody: TDIGlobalPak_Body; pResponsePack: PByte; nResponsePakLen: Word;
  pLogicDataPack: PByte; nDataPakLen: WORD; pLogicDataPackEx: PByte; nDataPakLenEx: Cardinal);
begin
  m_LogicLock.Lock;
  WriteTailDataPacket(@m_PackHead, DL_GLOBALPAK_HEAD_LEN);
  WriteTailDataPacket(@m_PackBody, DL_GLOBALPAK_BODY_LEN);
  WriteTailDataPacket(pResponsePack, nResponsePakLen);
  WriteTailDataPacket(pLogicDataPack, nDataPakLen);
  WriteTailDataPacket(pLogicDataPackEx, nDataPakLenEx, True);
  m_LogicLock.UnLock;
end;

procedure TIDataPacketSend.AddWSASendBufToQueue(FSendBuffer: TIBuffer);
begin
  if Assigned(FOnSendBufToQueue) then FOnSendBufToQueue(FSendBuffer);
end;

end.
